<?php

declare(ENCODING = 'UTF-8');

/*                                                                        *
 * This script is part of the TYPO3 project - inspiring people to share!  *
 *                                                                        *
 * TYPO3 is free software; you can redistribute it and/or modify it under *
 * the terms of the GNU General Public License version 2 as published by  *
 * the Free Software Foundation.                                          *
 *                                                                        *
 * This script is distributed in the hope that it will be useful, but     *
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-    *	
 * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General      *
 * Public License for more details.                                       *
 *                                                                        */	

/**
 * @package auxo
 * @subpackage AOP
 * @version $Id$
 */

/**	
 * The AOP Component Proxy Builder
 *	
 * @package auxo
 * @subpackage AOP
 * @version $Id$	
 * @copyright Copyright belongs to the respective authors
 * @author andreas.horn@extronaut.de
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License, version 2
 */
class tx_auxo_AOPManager {
		
	private $aspects;
	
	private $parser;
	
	private $comparator;
	
	private $builder;
	
	private $cache;
	
	private $registry;
	
	/**
	 * Flag that indicates caching of metadata 
	 *
	 * @var boolean
	 */
	private $caching = true;
	
	/**
	 * Creates an new AOP Manager
	 *
	 * @param tx_auxo_AOPParser $parser
	 */
	public function __construct(tx_auxo_AOPParser $parser) {
		$this->aspects = $parser->parse();		
		$this->registry = new tx_auxo_arrayObject(); 
	}

	/**
	 * Set caching of AOP metadata
	 *
	 * @param boolean $caching
	 */
	public function setCaching($caching) {
		$this->caching = $caching;
	}
	
	/**
	 * Returns true if a class has been adviced
	 *
	 * Classes have to be processed before this method shall be used
	 * 
	 * @param string $classname
	 * @return boolean $adviced
	 */
	public function isAdviced($classname) {
		if ($this->registry->has($classname)) {
			return true;
		}
		
		if ($this->caching) {
			if ($this->cache->has($classname)) {
				$this->registry->set($classname, $this->cache->get($classname));
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * Processes a class and create a proxy if any aspects is matching
	 *
	 * @param string $classname
	 * @return boolean $adviced 
	 */
	public function processClass($classname) {
		if ($this->isAdviced($classname)) {
			return true;
		}
		
		$matches = $this->matchAspects($classname);
		if (count($matches) == 0) {
			return false;
		}
		
		$targetClass = $this->getProxyClassname($classname);
		$code = $this->builder->build($classname, $proxyClass, $matches);
		$matches->set('targetClass', $targetClass);
		$this->writeClassFile($targetClass);
		
		if ($this->caching) {
			$this->cache->add($classname, $matches);
		}
		
		$this->registerClass($classname, $matches);
		return true;
	}
	
	/**
	 * Returns metadata of an adviced class
	 *
	 * @param string $classname
	 * @return tx_auxo_arrayObject $metadata
	 */
	public function getMetadata($classname) {
		$this->registry->get($classname);
	}
	
	/**
	 * Registered a class with its advice meta data
	 *
	 * @param unknown_type $class
	 * @param unknown_type $matches
	 */
	private function registerClass($class, $matches) {
		$this->registry->set($class, $matches);
	}
	
	/**
	 * Matchs a class with all registered aspects
	 *
	 * Metadata are return with all matching aspects and pointcuts
	 * 
	 * @param string $classname
	 * @return tx_auxo_HashArray $metadata
	 */
	private function matchAspects($classname) {
		$metadata = new tx_auxo_HashArray();
		foreach($this->aspects as $aspect) {
			
			foreach ($aspect->getPointcuts() as $pointcut) {
				if (($matches = $this->comparator->compare($classname, $pointcut))) {
					$joinpoints = array_merge($joinpoints, $matches);
				}
			}
			if (isset($pointcuts)) {
				$match = new tx_auxo_arrayObject();
				$match->set('aspect', $aspect);
				$match->set('joinpoints', $jointpoints);
				$metadata->add($match);
				unset($joinpoints);
			}
		}
		return $metadata;
	}

	/**
	 * Writes a proxy class to a file
	 *
	 * @param string $classname
	 * @param string $code
	 */
	private function writeClassFile($classname, $code) {
		t3lib_div::writeFileToTypo3tempDir($code, 'class.' . $this->getTargetClass($classname). '.php');
	}
	
	/**
	 * Returns a proxy class name
	 *
	 * @param string $classname
	 * @return string $proxClassname
	 */
	
	private function getTargetClass($classname) {
		return 'AOPProxy_' . $classname;
	}
}
?>